package com.xlh.virtualization.service.impl;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.xlh.dokka.api.DokkaMagicService;
import com.xlh.exception.common.GlobalException;
import com.xlh.virtualization.dao.JudgeMapper;
import com.xlh.virtualization.dao.helper.ContainerMapperHelper;
import com.xlh.virtualization.domain.*;
import com.xlh.virtualization.domain.example.JudgeExample;
import com.xlh.virtualization.exception.CheckException;
import com.xlh.virtualization.po.JudgeSavePO;
import com.xlh.virtualization.service.ExperimentJudgeService;
import com.xlh.virtualization.vo.JudgeInfoVO;
import com.xlh.virtualization.vo.JudgeVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * @author cheer
 */
@Service
@Slf4j
public class ExperimentJudgeServiceImpl implements ExperimentJudgeService {

    @Autowired
    private JudgeMapper judgeMapper;

    @Autowired
    private ContainerMapperHelper containerMapperHelper;

    @Autowired
    private DokkaMagicService dokkaMagicService;

    @Override
    public void saveJudge(JudgeSavePO judgeSavePO) {
        if (StringUtils.trimToNull(judgeSavePO.getScript()) == null) {
            JudgeExample example = new JudgeExample();
            example.or().andTypeEqualTo(judgeSavePO.getType())
                    .andContextIdEqualTo(judgeSavePO.getContextId())
                    .andDeletedEqualTo(false);
            Judge judge = new Judge();
            judge.setDeleted(true);
            judgeMapper.updateByExampleSelective(judge, example);
            return;
        }

        JudgeExample example = new JudgeExample();
        example.or().andTypeEqualTo(judgeSavePO.getType())
                .andContextIdEqualTo(judgeSavePO.getContextId())
                .andDeletedEqualTo(false);
        List<Judge> judgeList = judgeMapper.selectByExample(example);

        if (CollectionUtils.isEmpty(judgeList)) {
            Judge judge = new Judge();
            judge.setType(judgeSavePO.getType());
            judge.setContextId(judgeSavePO.getContextId());
            judge.setScript(judgeSavePO.getScript());
            judge.setAnswer(judgeSavePO.getAnswer());
            judge.setSkipped(judgeSavePO.getSkipped());
            judgeMapper.insertSelective(judge);
        } else {
            for (Judge judge : judgeList) {
                judge.setScript(judgeSavePO.getScript());
                judge.setAnswer(judgeSavePO.getAnswer());
                judge.setSkipped(judgeSavePO.getSkipped());
                judge.setUpdateTime(new Date());
                judgeMapper.updateByPrimaryKey(judge);
            }
        }

    }

    @Override
    public void runJudge(Integer type, Long contextId, Long containerId) {
        Container container = containerMapperHelper.getContainerById(containerId);
        if (container == null) {
            throw new GlobalException("虚拟机不存在");
        }

        JudgeExample example = new JudgeExample();
        example.or().andTypeEqualTo(type)
                .andContextIdEqualTo(contextId)
                .andDeletedEqualTo(false);
        List<Judge> judgeList = judgeMapper.selectByExample(example);

        if (CollectionUtils.isEmpty(judgeList)) {
            throw new GlobalException("评测脚本不存在");
        }

        Judge judge = judgeList.get(0);
        String result = dokkaMagicService.judge(container.getDokkaId(), judge.getScript());

        if (StringUtils.equalsIgnoreCase(StringUtils.trimToNull(judge.getAnswer()), StringUtils.trimToNull(result))) {
            return;
        }

        throw new CheckException(result);
    }

    @Override
    public JudgeInfoVO existsJudge(Integer type, Long contextId) {
        JudgeExample example = new JudgeExample();
        example.or().andTypeEqualTo(type)
                .andContextIdEqualTo(contextId)
                .andDeletedEqualTo(false);
        List<Judge> judgeList = judgeMapper.selectByExample(example);

        if (CollectionUtils.isEmpty(judgeList)) {
            JudgeInfoVO judgeInfoVO = new JudgeInfoVO();
            judgeInfoVO.setExists(false);
            return judgeInfoVO;
        }

        Judge judge = judgeList.get(0);
        if (StringUtils.isBlank(judge.getScript())) {
            JudgeInfoVO judgeInfoVO = new JudgeInfoVO();
            judgeInfoVO.setExists(false);
            return judgeInfoVO;
        }

        JudgeInfoVO judgeInfoVO = new JudgeInfoVO();
        judgeInfoVO.setExists(true);
        judgeInfoVO.setSkipped(judge.getSkipped());
        return judgeInfoVO;
    }

    @Override
    public JudgeVO getJudge(Integer type, Long contextId) {
        JudgeExample example = new JudgeExample();
        example.or().andTypeEqualTo(type)
                .andContextIdEqualTo(contextId)
                .andDeletedEqualTo(false);
        List<Judge> judgeList = judgeMapper.selectByExample(example);

        if (CollectionUtils.isEmpty(judgeList)) {
            return new JudgeVO();
        }

        Judge judge = judgeList.get(0);
        JudgeVO judgeVO = new JudgeVO();
        judgeVO.setScript(judge.getScript());
        judgeVO.setAnswer(judge.getAnswer());
        judgeVO.setSkipped(judge.getSkipped());
        return judgeVO;
    }

    @Override
    public void delete(Integer type, List<Long> contextIds) {
        if (CollectionUtils.isEmpty(contextIds)) {
            return;
        }

        JudgeExample example = new JudgeExample();
        example.or().andTypeEqualTo(type)
                .andContextIdIn(contextIds)
                .andDeletedEqualTo(false);
        Judge judge = new Judge();
        judge.setDeleted(true);
        judgeMapper.updateByExampleSelective(judge, example);
    }

    @Override
    public void trueDelete(Integer type, List<Long> contextIds) {
        if (CollectionUtils.isEmpty(contextIds)) {
            return;
        }

        JudgeExample example = new JudgeExample();
        example.or().andTypeEqualTo(type)
                .andContextIdIn(contextIds);
        judgeMapper.deleteByExample(example);
    }

    @Override
    public void copy(Integer type, Long sourceContextId, Long targetContextId) {
        JudgeExample example = new JudgeExample();
        example.or().andTypeEqualTo(type)
                .andContextIdEqualTo(sourceContextId)
                .andDeletedEqualTo(false);
        List<Judge> judgeList = judgeMapper.selectByExample(example);

        if (CollectionUtils.isEmpty(judgeList)) {
            return;
        }

        Judge judge = judgeList.get(0);

        Judge insertJudge = new Judge();
        insertJudge.setType(type);
        insertJudge.setContextId(targetContextId);
        insertJudge.setScript(judge.getScript());
        insertJudge.setAnswer(judge.getAnswer());
        insertJudge.setSkipped(judge.getSkipped());
        judgeMapper.insertSelective(insertJudge);
    }

    @Override
    public void transport(JdbcTemplate jdbcTemplate, Map<Long, Long> taskIdMap, Map<Long, Long> stepIdMap) {
        transport(jdbcTemplate, taskIdMap, Judge.TASK_TYPE);
        transport(jdbcTemplate, stepIdMap, Judge.STEP_TYPE);
    }

    private void transport(JdbcTemplate jdbcTemplate, Map<Long, Long> contextIdMap, Integer type) {
        String sql = "select id, type, context_id, script, answer, is_skipped as skipped " +
                "from vm_judge " +
                "where is_deleted = 0 and type = " + type + " and context_id in (:contextIds)";

        // 取出远程数据库的任务id
        List<Long> contextIds = Lists.newArrayList(contextIdMap.keySet());
        if (CollectionUtils.isEmpty(contextIds)) {
            return;
        }

        Map<String, Object> paramMap = Maps.newHashMap();
        paramMap.put("contextIds", contextIds);

        RowMapper<Judge> rowMapper = new BeanPropertyRowMapper<>(Judge.class);

        NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
        List<Judge> judgeList = namedParameterJdbcTemplate.query(sql, paramMap, rowMapper);

        for (Judge judge : judgeList) {
            Long target = contextIdMap.get(judge.getContextId());
            if (target != null) {
                judge.setId(null);
                judge.setContextId(target);
                judgeMapper.insertSelective(judge);
            }
        }
    }

}
